/**   
 * @{#} XmlEngine.java Create on 2013-8-9 上午9:35:00   
 *   
 * Copyright (c) 2012 by Baidu.   
 */
package objtable.engine;

import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.PropertyException;
import javax.xml.bind.Unmarshaller;
import javax.xml.namespace.NamespaceContext;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;

import objtable.engine.xmlnode.CalltaskNode;
import objtable.engine.xmlnode.ComputeNode;
import objtable.engine.xmlnode.DbPortNode;
import objtable.engine.xmlnode.DbparasNode;
import objtable.engine.xmlnode.FilterNode;
import objtable.engine.xmlnode.FuncNode;
import objtable.engine.xmlnode.GroupNode;
import objtable.engine.xmlnode.JoinNode;
import objtable.engine.xmlnode.MatchNode;
import objtable.engine.xmlnode.OrderNode;
import objtable.engine.xmlnode.SqlRunnerNode;
import objtable.engine.xmlnode.TableNode;
import objtable.engine.xmlnode.gen.Calltask;
import objtable.engine.xmlnode.gen.Compute;
import objtable.engine.xmlnode.gen.Dbpara;
import objtable.engine.xmlnode.gen.Dbparas;
import objtable.engine.xmlnode.gen.Dbport;
import objtable.engine.xmlnode.gen.Filter;
import objtable.engine.xmlnode.gen.Func;
import objtable.engine.xmlnode.gen.Group;
import objtable.engine.xmlnode.gen.Join;
import objtable.engine.xmlnode.gen.Match;
import objtable.engine.xmlnode.gen.Nodes;
import objtable.engine.xmlnode.gen.Order;
import objtable.engine.xmlnode.gen.Sqlrunner;
import objtable.engine.xmlnode.gen.Table;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;

import com.google.common.base.Charsets;
import com.google.common.io.Files;

/**
 * @author <a href="mailto:zhangbo07@baidu.com">zhangbo07</a>
 * @version 1.0
 */

public class XmlEngine {
	private static Logger log = LoggerFactory.getLogger(XmlEngine.class);

	private Unmarshaller unmarshaller;
	private Marshaller marshaller;
	private String txt;

	public XmlEngine(String xmltxt) throws JAXBException, XMLStreamException {
		JAXBContext context = JAXBContext.newInstance(Dbport.class.getPackage()
				.getName());
		unmarshaller = context.createUnmarshaller();
		marshaller = context.createMarshaller();
		marshaller.setProperty(Marshaller.JAXB_FRAGMENT, true);
		txt = xmltxt;
	}

	private static HashMap<String, Object> id2Obj = new HashMap<String, Object>();
	private static HashMap<String, String> dbparas = new HashMap<String, String>();
	private static String yesterday;

	public static void putDbparas(String key, String value) {
		dbparas.put(key, value);
	}

	public static void putIdObj(String id, Object obj) {
		id2Obj.put(id, obj);
	}

	public static Object getObjById(String id) {
		return id2Obj.get(id);
	}

	public static void main(String[] args) throws JAXBException, IOException,
			ParseException, XMLStreamException {
		String name = null;
		if (args == null) {
			System.exit(1);
		} else if (args.length == 1) {
			name = args[0];
		} else if (args.length == 2) {
			name = args[0];
			yesterday = args[1];
		}

		String filename = "./xmlscript/" + name + ".xml";

		SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd_HHmmss");
		MDC.put("filename", args[0] + "_" + sdf.format(new Date()));

		String txt = Files.toString(new File(filename), Charsets.UTF_8);
		XmlEngine engine = new XmlEngine(txt);

		engine.runXml();

		System.exit(0);
	}

	public void runXml() throws JAXBException, PropertyException,
			ParseException {
		// 全局变量的替换
		String txt = preProcess(this.txt);

		Nodes o1 = (Nodes) unmarshaller.unmarshal(new StringReader(txt));
		for (Object o : o1.getDbportOrDbparasOrFunc()) {
			StringWriter sw = new StringWriter();
			marshaller.marshal(o, sw);
			log.info("begin:" + sw.toString());

			if (o instanceof Dbport) {
				DbPortNode dpn = new DbPortNode();
				dpn.setDbPort((Dbport) o);
				dpn.exeNode();
			} else if (o instanceof Func) {
				FuncNode fn = new FuncNode();
				fn.setFunc((Func) o);
				fn.exeNode();
			} else if (o instanceof Table) {
				TableNode tn = new TableNode();
				tn.setTable((Table) o);
				tn.exeNode();
			} else if (o instanceof Match) {
				MatchNode mn = new MatchNode();
				mn.setMatch((Match) o);
				mn.exeNode();
			} else if (o instanceof Compute) {
				ComputeNode cn = new ComputeNode();
				cn.setCompute((Compute) o);
				cn.exeNode();
			} else if (o instanceof Order) {
				OrderNode on = new OrderNode();
				on.setOrder((Order) o);
				on.exeNode();
			} else if (o instanceof Filter) {
				FilterNode on = new FilterNode();
				on.setFilter((Filter) o);
				on.exeNode();
			} else if (o instanceof Join) {
				JoinNode on = new JoinNode();
				on.setJoin((Join) o);
				on.exeNode();
			} else if (o instanceof Group) {
				GroupNode on = new GroupNode();
				on.setGroup((Group) o);
				on.exeNode();
			} else if (o instanceof Sqlrunner) {
				SqlRunnerNode on = new SqlRunnerNode();
				on.setRun((Sqlrunner) o);
				on.exeNode();
			} else if (o instanceof Calltask) {
				CalltaskNode on = new CalltaskNode();
				on.setCalltask((Calltask) o);
				on.exeNode();
			} else if (o instanceof Dbparas) {
				Dbparas ps = (Dbparas) o;
				boolean needredo = false;
				for (Dbpara p : ps.getDbpara()) {
					if (!dbparas.containsKey(p.getName())) {
						// 更新paras并重新加载执行当前xml
						DbparasNode on = new DbparasNode();
						on.setDbparas(ps);
						on.exeNode();
						needredo = true;
					}
				}
				if (needredo) {
					runXml();
					break;
				}
			}

			log.info("over:" + sw.toString());
		}
	}

	private static Date lastDayOfMonth(Date date) {
		Calendar cal = Calendar.getInstance();
		cal.setTime(date);
		cal.set(Calendar.DAY_OF_MONTH, 1);
		cal.roll(Calendar.DAY_OF_MONTH, -1);
		return cal.getTime();
	}

	// 替换全局变量
	private static String preProcess(String txt) throws ParseException {
		// 全局常量
		// yesterday
		Calendar c = null;
		if (yesterday == null) {
			c = Calendar.getInstance();
			c.add(Calendar.DATE, -1);
			yesterday = new SimpleDateFormat("yyyyMMdd").format(c.getTime());
		} else {
			Date yd = new SimpleDateFormat("yyyyMMdd").parse(yesterday);
			c = Calendar.getInstance();
			c.setTime(yd);
		}

		String lastdayofmonth = new SimpleDateFormat("yyyyMMdd")
				.format(lastDayOfMonth(c.getTime()));
		String islastdayofmonth = "false";
		if (yesterday.equals(lastdayofmonth)) {
			islastdayofmonth = "true";
		}

		String month = new SimpleDateFormat("yyyyMM").format(c.getTime());
		String monthdays = c.getActualMaximum(Calendar.DATE) + "";

		// 字符替换必须按长度重大到小执行
		txt = txt.replaceAll("#islastdayofmonth", islastdayofmonth);
		txt = txt.replaceAll("#yesterday", yesterday);
		txt = txt.replaceAll("#monthdays", monthdays);
		txt = txt.replaceAll("#month", month);

		// 数据库变量长度重大到小排序
		Set<String> keys = dbparas.keySet();
		String[] kys = new String[keys.size()];
		kys = keys.toArray(kys);
		Arrays.sort(kys, new Comparator<String>() {
			public int compare(String o1, String o2) {
				return o2.length() - o1.length();
			}
		});
		// 字符长度重大到小排序

		for (String tk : kys) {
			txt = txt.replaceAll("#" + tk, dbparas.get(tk));
		}
		return txt;
	}
}
